package com.zbs.framework.swagger.aop;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zbs.framework.swagger.annotations.*;
import com.zbs.framework.swagger.config.SwaggerBusinessRuntimeException;
import com.zbs.framework.swagger.constants.SwaggerApiConstant;
import com.zbs.framework.swagger.enums.RequestType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Aspect
@Component
@Order(1)
@EnableAspectJAutoProxy
@EnableAsync
@Slf4j
public class ParamsBaseAspect implements IBaseAspect{
    @Override
    @Before(value = "pointCut()",argNames = "joinPoint")
    public void before(JoinPoint joinPoint) throws Throwable {
        if (SwaggerApiConstant.enable.get(SwaggerApiConstant.enableCheckParams) != null && SwaggerApiConstant.enable.get(SwaggerApiConstant.enableCheckParams)) {
            handleLog(joinPoint,null);
        }
    }

    /**
     * 配置织入点
     */
    @Pointcut("@annotation(com.zbs.framework.swagger.annotations.ApiOperation)")
    public void pointCut() {
    }

    @Override
    public void after(JoinPoint joinPoint) throws Throwable {

    }

    @Override
    @AfterReturning(pointcut = "pointCut()", argNames = "joinPoint,returnObj", returning = "returnObj")
    public void afterReturning(JoinPoint joinPoint, Object returnObj) throws Throwable {
    }

    @Override
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) throws Throwable {
    }

    @Override
    public Object around(ProceedingJoinPoint pJoinPoint) {
        return null;
    }

    protected void handleLog(final JoinPoint joinPoint, final Throwable e) throws ClassNotFoundException {
        MethodSignature signature = ((MethodSignature) joinPoint.getSignature());
        //得到拦截的方法
        Method method = signature.getMethod();
        ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
        ApiIgnore apiIgnore = method.getAnnotation(ApiIgnore.class);
        List<String> apiIgnoreFiled = new ArrayList<>();
        if (apiIgnore != null) {
            apiIgnoreFiled = Arrays.stream(apiIgnore.value()).collect(Collectors.toList());;
        }

        if (apiOperation != null) {
            RequestType requestType = apiOperation.requestType();
            Object[] args = joinPoint.getArgs();
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                Class<?> type = parameter.getType();
                ApiModel apiModel = type.getAnnotation(ApiModel.class);
                if (apiModel != null) {
                    this.checkModel(type,requestType,args[i],apiIgnoreFiled);
                }
                ApiParam apiParam = parameter.getAnnotation(ApiParam.class);
                if (apiParam != null && apiParam.required() && !apiIgnoreFiled.contains(parameter.getName())) {
                    if (ObjectUtils.isEmpty(args[i])) {
                        throw new SwaggerBusinessRuntimeException(-1,String.format("`%s`不能为空",apiParam.value()));
                    }
                }
            }
        }
    }

    public void checkModel(Class<?> clazz, RequestType requestType, Object arg, List<String> apiIgnoreFiled) throws ClassNotFoundException {
        String name = "";
        ApiModel annotation = clazz.getAnnotation(ApiModel.class);
        if (annotation != null) {
            name =  clazz.getSuperclass().getName();
            Class<?> superclass = Class.forName(name);
            ApiModel apiModel = superclass.getAnnotation(ApiModel.class);
            if (apiModel != null) {
                this.checkFiled(superclass,requestType,arg,apiIgnoreFiled);
            }
            this.checkFiled(clazz,requestType,arg, apiIgnoreFiled);
        }
    }

    public void checkFiled(Class<?> clazz, RequestType requestType, Object arg, List<String> apiIgnoreFiled) throws ClassNotFoundException {
        ApiModel apiModel = clazz.getAnnotation(ApiModel.class);
        if (apiModel != null) {
            JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(arg));
            Arrays.stream(clazz.getDeclaredFields()).forEach(field -> {
                ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                if (apiModelProperty != null && apiModelProperty.required() && !apiIgnoreFiled.contains(field.getName())) {
                    Object filedJsonObject = jsonObject.get(field.getName());
                    Class<?> type = field.getType();
                    ApiModel typeApiModel = type.getAnnotation(ApiModel.class);
                    if (typeApiModel != null) {
                        try {
                            this.checkFiled(type,requestType,filedJsonObject, apiIgnoreFiled);
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                    String array = this.isArray(field.getType().getName());
                    String listGenericType = this.getListGenericType(field.getGenericType());
                    // 判断是否是一维数组 和 判断是否是集合list
                    if (StringUtils.hasLength(array) || StringUtils.hasLength(listGenericType)) {
                        JSONArray jsonArray = jsonObject.getJSONArray(field.getName());
                        if (jsonArray.isEmpty()) {
                            throw new SwaggerBusinessRuntimeException(-1,String.format("`%s`不能为空",apiModelProperty.value()));
                        }
                    }
                    Object object = jsonObject.getObject(field.getName(), type);
                    if (ObjectUtils.isEmpty(object)) {
                        throw new SwaggerBusinessRuntimeException(-1,String.format("`%s`不能为空",apiModelProperty.value()));
                    }
                }

            });
        }
    }

    /**
     * 判断数组中的类型
     *
     * @param genericType
     * @return
     */
    private String getListGenericType(Type genericType){
        if (null != genericType) {
            try {
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) genericType;
                    // 得到泛型里的class类型对象
                    Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                    return actualTypeArgument.getName();
                }
            } catch (Exception e) {
                return "";
            }
        }
        return "";
    }


    private String isArray(String genericType) {
        Class<?> clazz = null;
        try {
            // 完整类名带有 . 的是对象类型，如果没有则是基本数据类型 java.lang.String   int string long...
            if (genericType.indexOf('.') != -1){
                clazz = Class.forName(genericType);
                if (clazz.isArray()) {
                    return clazz.getComponentType().getName();
                }
            }
        } catch (ClassNotFoundException e) {
            return "";
        }
        return "";
    }

    private String getTGenericType(String genericType){
        Class<?> clazz = null;
        try {
            // 完整类名带有 . 的是对象类型，如果没有则是基本数据类型 java.lang.String   int string long...
            if (genericType.indexOf('.') != -1) {
                clazz = Class.forName(genericType);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (clazz != null) {
            ApiModel apiModel = clazz.getAnnotation(ApiModel.class);
            if(apiModel != null) {
                return clazz.getName();
            }
        }
        return "";
    }


}
